package controller

import (
	"context"
	"fmt"
	"github.com/prometheus/client_golang/prometheus"
	"go.uber.org/zap"
	"v8s-controller/internal/db"
	"v8s/logger"
)

type (
	TaskMetricsController struct {
		log                            *zap.SugaredLogger
		projects                       *db.ProjectsRepository
		taskStatusGauge                *prometheus.GaugeVec
		taskClusterResourceStatusGauge *prometheus.GaugeVec
	}
)

const (
	taskMetricsControllerName = "task-metrics"
)

func NewTaskMetricsController(projects *db.ProjectsRepository) *TaskMetricsController {
	taskStatusGauge := prometheus.NewGaugeVec(prometheus.GaugeOpts{
		Namespace: "v8s",
		Subsystem: "task",
		Name:      "status",
		Help:      "Total number of task in status",
	}, []string{"task_rn", "task_ns", "status"})

	taskClusterResourceStatusGauge := prometheus.NewGaugeVec(prometheus.GaugeOpts{
		Namespace: "v8s",
		Subsystem: "task",
		Name:      "cluster_resource_status",
		Help:      "Total number of tasks cluster resource in status",
	}, []string{"task_rn", "task_ns", "cluster", "resource", "status"})

	prometheus.MustRegister(taskStatusGauge, taskClusterResourceStatusGauge)

	return &TaskMetricsController{
		log:                            logger.Logger().Named("task-metrics-controller").Sugar(),
		projects:                       projects,
		taskStatusGauge:                taskStatusGauge,
		taskClusterResourceStatusGauge: taskClusterResourceStatusGauge,
	}
}

func (t *TaskMetricsController) Name() string {
	return taskMetricsControllerName
}

func (t *TaskMetricsController) Execute(ctx context.Context) error {
	t.taskStatusGauge.Reset()
	t.taskClusterResourceStatusGauge.Reset()

	tasks, err := t.projects.ListTasksWithStatuses(ctx, 10)
	if err != nil {
		return fmt.Errorf("failed to list tasks with statuses: %w", err)
	}

	for task := range tasks {
		t.log.Debugf("handling metrics for task '%s'", task.RN)

		namespace, err := task.ID.ToNamespace()
		if err != nil {
			namespace = "failed-to-determine"
		}

		t.taskStatusGauge.WithLabelValues(task.RN.String(), string(namespace), string(task.Status)).Set(1)

		for name, cluster := range task.Clusters {
			resourceKindByStatusCount := map[string]map[string]int{}

			for _, resource := range cluster.Resources {
				statusByCount, ok := resourceKindByStatusCount[resource.Kind]
				if !ok {
					statusByCount = map[string]int{}
				}

				current := statusByCount[string(resource.Status)]
				statusByCount[string(resource.Status)] = current + 1

				resourceKindByStatusCount[resource.Kind] = statusByCount
			}

			for kind, statusByCount := range resourceKindByStatusCount {
				for status, count := range statusByCount {
					t.taskClusterResourceStatusGauge.WithLabelValues(task.RN.String(), string(namespace), string(name), kind, status).Set(float64(count))
				}
			}

		}
	}

	return nil
}
